package com.cancan.mp;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.google.common.collect.Maps;
import org.apache.commons.collections.MapUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.util.*;

/**
 *  演示例子，执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
 *
 * @author cancan
 * @since 2020-03-23
 */
public class CodeGenerator {


    /**
     * 上下文名称
     */
    private static String packageConfig = "plan";
    /**
     * 表前缀
     */
    private static String tablePrefixName = "dx_";
    /**
     * 数据库名称，用，隔开
     */
    private static String databases = "tb_test_student";
    /**
     * 是否覆盖原有文件
     */
    private static boolean isFileOverride = false;
    /**
     * 作者名称
     */
    private static String author = "ffzhou";
    /**
     * 是否打开文件
     */
    private static boolean isOpen = false;
    /**
     * 开启 BaseResultMap,默认值：false
     */
    private static boolean isBaseResultMap = true;
    /**
     * 开启 baseColumnList,默认值：false
     */
    private static boolean isBaseColumnList = true;
    /**
     * 是否在xml中添加二级缓存配置,默认值：false
     */
    private static boolean isEnableCache = false;
    /**
     * 实体属性 Swagger2 注解
     */
    private static boolean isSwagger2 = true;
    /**
     * 数据库路径
     * datasourceUrl=jdbc:sqlserver://192.168.83.50:1433;databaseName=cpmsdbv4.1;
     */
    private static String datasourceUrl = "jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2B8";
    /**
     * 数据库驱动
     * datasourceDriver=com.microsoft.sqlserver.jdbc.SQLServerDriver
     */
    private static String datasourceDriver = "com.mysql.jdbc.Driver";
    /**
     * 账号
     * datasourceUsername=sa
     */
    private static String datasourceUsername = "root";
    /**
     * 密码
     * datasourcePassword=karry
     */
    private static String datasourcePassword = "admin";
    /**
     * 父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名
     */
    private static String parentName = "com.pci.hjmos.emergency.command";
    /**
     * 数据库表映射到实体的命名策略 --- 数据库名
     * 0 no_change 不做任何改变，原样输出
     * 1 underline_to_camel 下划线转驼峰命名
     */
    private static int namingStrategy = 1;
    /**
     * 数据库表字段映射到实体的命名策略, 未指定按照 naming 执行 --- 字段名
     * 0 no_change 不做任何改变，原样输出
     * 1 underline_to_camel 下划线转驼峰命名
     */
    private static int columnNamingStrategy = 1;
    /**
     * isEntityLombokModel
     */
    private static boolean isEntityLombokModel = true;
    /**
     * 生成 @RestController 控制器
     */
    private static boolean isRestControllerStyle = true;
    /**
     * 【实体】是否生成字段常量（默认 false）
     */
    private static boolean isEntityColumnConstant = false;
    /**
     * 默认实体类贴上 @TableField
     */
    private static boolean isEntityTableFieldAnnotationEnable = true;
    /**
     * 驼峰转连字符
     */
    private static boolean isMappingHyphenStyle = true;

    /**
     * 自定义继承的Entity类全称，带包名
     */
    private static String superEntityClass = "";

    /**
     * 自定义继承的Controller类全称，带包名
     */
    private static String  superControllerClass = "com.pci.hjmos.framework.web.common.controller.BaseController";

    /**
     * 是否指定表前缀
     */
    private static boolean isTablePrefix = false;

    /**
     * id自动生成主键的方式
     * 0    AUTO(0),
     * 1    NONE(1),
     * 2    INPUT(2),
     * 3    ASSIGN_ID(3),
     * 4    ASSIGN_UUID(4
     */
    private static int idType = 0;

    /**
     * 使用使用hjmos
     * */
    private static Boolean isHjmos = true ;

    /**
     * 使用传统springboot项目
     * */
    private static Boolean isSpringboot = false ;

    /**
     * 配置模块与表
     */
    private static Map<String, List<String>> moduleTableMap = Maps.newHashMap();


    public static void main(String[] args) {

        for (String arg : args) {

            String[] keyValue = arg.split("=",2);

            switch (keyValue[0]){
                case "packageConfig": packageConfig = keyValue.length < 2 ? "": keyValue[1]; break;
                case "databases": databases = keyValue.length < 2 ? "": keyValue[1];break;
                case "isFileOverride": isFileOverride = Boolean.valueOf(keyValue[1]);break;
                case "author": author = keyValue.length < 2 ? "": keyValue[1];break;
                case "isOpen": isOpen = Boolean.valueOf(keyValue[1]);break;
                case "isBaseResultMap": isBaseResultMap = Boolean.valueOf(keyValue[1]);break;
                case "isBaseColumnList": isBaseColumnList = Boolean.valueOf(keyValue[1]);break;
                case "isEnableCache": isEnableCache = Boolean.valueOf(keyValue[1]);break;
                case "isSwagger2": isSwagger2 = Boolean.valueOf(keyValue[1]);break;
                case "datasourceUrl": datasourceUrl = keyValue.length < 2 ? "": keyValue[1];break;
                case "datasourceDriver": datasourceDriver = keyValue.length < 2 ? "": keyValue[1];break;
                case "datasourceUsername": datasourceUsername = keyValue.length < 2 ? "": keyValue[1];break;
                case "datasourcePassword": datasourcePassword = keyValue.length < 2 ? "": keyValue[1];break;
                case "parentName": parentName = keyValue.length < 2 ? "": keyValue[1];break;
                case "namingStrategy": namingStrategy = Integer.valueOf(keyValue[1]);break;
                case "columnNamingStrategy": columnNamingStrategy = Integer.valueOf(keyValue[1]);break;
                case "isEntityLombokModel": isEntityLombokModel = Boolean.valueOf(keyValue[1]);break;
                case "isRestControllerStyle": isRestControllerStyle = Boolean.valueOf(keyValue[1]);break;
                case "isEntityColumnConstant": isEntityColumnConstant = Boolean.valueOf(keyValue[1]);break;
                case "isEntityTableFieldAnnotationEnable": isEntityTableFieldAnnotationEnable = Boolean.valueOf(keyValue[1]);break;
                case "isMappingHyphenStyle": isMappingHyphenStyle = Boolean.valueOf(keyValue[1]);break;
                case "superEntityClass": superEntityClass = keyValue.length < 2 ? "": keyValue[1];break;
                case "superControllerClass": superControllerClass = keyValue.length < 2 ? "": keyValue[1];break;
                case "isTablePrefix": isTablePrefix = Boolean.valueOf(keyValue[1]);break;
                case "tablePrefixName": tablePrefixName = keyValue.length < 2 ? "": keyValue[1];break;
                case "idType": idType = Integer.valueOf(keyValue[1]);break;
                case "isHjmos": isHjmos = Boolean.valueOf(keyValue[1]);break;
                case "isSpringboot": isSpringboot = Boolean.valueOf(keyValue[1]);break;

                default:
                    System.out.println(keyValue[0] + ", 找不到对应的数据");
                    break;
            }
        }

        String[] split = databases.split(",");

        //key为当前所有文件放置的包名，如果为空指的就是packageConfig父类目录下，value为在该包名下生成的表，可以多张表，存放在list
        moduleTableMap.put(packageConfig, Arrays.asList(split));
        try{
            autoGenerator();
        }catch (Exception ex){
            System.out.println("输入参数有误 "+ ex.getMessage());
        }
    }

    /**
     * mybatis-plus代码生成器主要是由以下几部分组成
     * 1. 代码生成器基本配置
     * 2. 数据源 dataSourceConfig 配置
     * 3. 数据库表配置,一般不配置
     * 4. 包名配置
     * 5. 模板配置
     * 6. 全局策略 globalConfig 配置
     * 7. 注入 injectionConfig 配置
     *
     * 执行代码生成操作
     */
    private static void autoGenerator(){
        if(MapUtils.isNotEmpty(moduleTableMap)){
            for(Map.Entry<String,List<String>> entry : moduleTableMap.entrySet()){
                List<String> tables = entry.getValue();
                if(CollectionUtils.isNotEmpty(tables)){
                    tables.stream().forEach(e->{
                        // 代码生成器
                        AutoGenerator mpg = new AutoGenerator();
                        // 1. 代码生成器基本配置
                        String projectPath = globalConfig(mpg);
                        // 2. 数据源 dataSourceConfig 配置
                        dataSourceConfig(mpg);
                        // 4. 包名配置
                        PackageConfig pc =packageConfig(mpg,entry.getKey());
                        // 5. 模板配置
                        injectionConfig(mpg,pc,projectPath);
                        // 6. 全局策略 globalConfig 配置
                        strategyConfig(mpg,pc,e);
                        //7. 注入 injectionConfig 配置
                        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
                        //执行代码生成操作
                        mpg.execute();
                    });
                }
            }
        }else{
            throw new RuntimeException("请配置模块");
        }
    }

    /**
     * 1. 代码生成器基本配置 设置全局配置.
     *
     * @param mpg 自动代码生成对象
     * @return 设置全局配置
     */
    private static String globalConfig(AutoGenerator mpg){
        //设置全局配置
        GlobalConfig gc = new GlobalConfig();

        //获取当前路径
        String projectPath = System.getProperty("user.dir");

        System.out.println(projectPath);
        //自定义输出文件
        //配置 FileOutConfig 指定模板文件、输出文件达到自定义文件生成目的
        gc.setOutputDir(projectPath + "/src/main/java");

        //是否覆盖已有文件，默认值：false
        gc.setFileOverride(isFileOverride);

        //开发人员
        gc.setAuthor(author);

        //是否打开输出目录
        gc.setOpen(isOpen);

        //只使用 java.util.date 代替
        gc.setDateType(DateType.ONLY_DATE);

        //指定生成的主键的ID类型
        switch (idType){
            case 1:gc.setIdType(IdType.NONE);break;
            case 2:gc.setIdType(IdType.INPUT);break;
            case 3:gc.setIdType(IdType.ASSIGN_ID);break;
            case 4:gc.setIdType(IdType.ASSIGN_UUID);break;
            default: gc.setIdType(IdType.AUTO);
        }

        //开启 BaseResultMap,默认值：false
        gc.setBaseResultMap(isBaseResultMap);

        //开启 baseColumnList,默认值：false
        gc.setBaseColumnList(isBaseColumnList);

        //是否在xml中添加二级缓存配置,默认值：false
        gc.setEnableCache(isEnableCache);

        mpg.setGlobalConfig(gc);

        //实体属性 Swagger2 注解
        gc.setSwagger2(isSwagger2);

        return projectPath;
    }

    /**
     * 2. 数据源 dataSourceConfig 配置
     *
     * @param mpg 自动代码生成对象
     */
    private static void dataSourceConfig(AutoGenerator mpg){
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(datasourceUrl);
        // dsc.setSchemaName("public");
        dsc.setDriverName(datasourceDriver);
        //账号
        dsc.setUsername(datasourceUsername);
        //密码
        dsc.setPassword(datasourcePassword);

        //username: mics
        //password: mics
        //url: jdbc:oracle:thin:@//10.40.3.5:1521/micstcz
        //driver-class-name: oracle.jdbc.driver.OracleDriver

        mpg.setDataSource(dsc);
    }

    /**
     * 4. 包名配置, 设置包配置.
     *
     * @param mpg 自动代码生成对象
     * @param moduleName 当前包的名字
     * @return 包的配置
     */
    private static PackageConfig packageConfig(AutoGenerator mpg, String moduleName){
        // 包配置
        PackageConfig pc = new PackageConfig();

        //父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名
        pc.setParent(parentName);

        //父包模块名
        pc.setModuleName(moduleName);

        mpg.setPackageInfo(pc);
        return pc;
    }

    /**
     * 5. 模板配置, 自定义配置
     *
     * @param mpg 自动代码生成对象
     * @param pc 包配置
     * @param projectPath 设置全局配置
     */
    private static void injectionConfig(AutoGenerator mpg, PackageConfig pc, String projectPath){

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>(5);
                map.put("hjmos", isHjmos.toString());
                map.put("springboot",isSpringboot.toString());
                this.setMap(map);
            }
        };

        // 如果模板引擎是 freemarker
//        String templatePath = "/templates/mapper.xml.ftl";

        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
//        focList.add(new FileOutConfig(templatePath) {
//            @Override
//            public String outputFile(TableInfo tableInfo) {
//                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
//                String outPath = projectPath+"/src/main/java/"+pc.getParent().replaceAll("\\.","/")
//                        +"/mapper/"+tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
//                return outPath;
//            }
//        });


//        cfg.setFileCreate(new IFileCreate() {
//            @Override
//            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
//                // 判断自定义文件夹是否需要创建
//                checkDir("调用默认方法创建的目录，自定义目录用");
//                if (fileType == FileType.MAPPER) {
//                    // 已经生成 mapper 文件判断存在，不想重新生成返回 false
//                    return !new File(filePath).exists();
//                }
//                // 允许生成模板文件
//                return true;
//            }
//        });


        String absolutePath = null;

        File directory = new File("");//设定为当前文件夹
        try{
            absolutePath = directory.getAbsolutePath();
        }catch(Exception e){
            e.printStackTrace();
        }

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        TemplateConfig templateConfig = new TemplateConfig();

        if(isHjmos){
            templateConfig.setEntity("custom-template/entity.java");
            // 配置自定义输出模板
            //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
            // templateConfig.setEntity("templates/entity2.java");
            // templateConfig.setService();
            // templateConfig.setController();
            templateConfig.setController("hjmos-template/controller.java");
            templateConfig.setService("hjmos-template/service.java");
            templateConfig.setServiceImpl("hjmos-template/serviceImpl.java");
            templateConfig.setXml(null);
        }

        if(isSpringboot){
            templateConfig.setEntity("custom-template/entity.java");
            // 配置自定义输出模板
            //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
            // templateConfig.setEntity("templates/entity2.java");
            // templateConfig.setService();
            // templateConfig.setController();
            templateConfig.setController("custom-template/controller.java");
            templateConfig.setService("custom-template/service.java");
            templateConfig.setServiceImpl("custom-template/serviceImpl.java");
            templateConfig.setXml("custom-template/mapper.xml");
            templateConfig.setMapper("custom-template/mapper.java");
        }

        mpg.setTemplate(templateConfig);
    }

    /**
     * 6. 全局策略 globalConfig 配置,策略配置
     *
     * @param mpg 自动代码生成对象
     * @param pc 包配置
     * @param tableName 表格名字
     */
    private static void strategyConfig(AutoGenerator mpg, PackageConfig pc, String tableName){
        //策略配置
        StrategyConfig strategy = new StrategyConfig();

        NamingStrategy namingStrategyModel = NamingStrategy.underline_to_camel;

        if(namingStrategy == 0){
            namingStrategyModel = NamingStrategy.no_change;
        }

        //数据库表映射到实体的命名策略 --- 数据库名
        //no_change 不做任何改变，原样输出
        //underline_to_camel 下划线转驼峰命名

        strategy.setNaming(namingStrategyModel);

        //数据库表字段映射到实体的命名策略, 未指定按照 naming 执行 --- 字段名
        NamingStrategy columnNamingStrategyModel = NamingStrategy.underline_to_camel;

        if(columnNamingStrategy == 0){
            columnNamingStrategyModel = NamingStrategy.no_change;
        }

        strategy.setColumnNaming(columnNamingStrategyModel);

        //自定义继承的Entity类全称，带包名 --- 你自己的父类实体,没有就不用设置!
        strategy.setSuperEntityClass(superEntityClass);

        //【实体】是否为lombok模型（默认 false）
        strategy.setEntityLombokModel(isEntityLombokModel);

        //生成 @RestController 控制器
        strategy.setRestControllerStyle(isRestControllerStyle);

        // 自定义继承的Controller类全称，带包名 --- 你自己的父类控制器,没有就不用设置!
        strategy.setSuperControllerClass(superControllerClass);

        //需要包含的表名，当enableSqlFilter为false时，允许正则表达式（与exclude二选一配置）
        strategy.setInclude(tableName);

        //【实体】是否生成字段常量（默认 false）
        strategy.setEntityColumnConstant(isEntityColumnConstant);

        //默认实体类贴上 @TableField
        strategy.setEntityTableFieldAnnotationEnable(isEntityTableFieldAnnotationEnable);

        //驼峰转连字符
        strategy.setControllerMappingHyphenStyle(isMappingHyphenStyle);

        if(isTablePrefix){
            //表前缀
            strategy.setTablePrefix(StringUtils.isEmpty(tablePrefixName) ? pc.getModuleName() + "_" : tablePrefixName);
        }

        mpg.setStrategy(strategy);
    }

}